To be able to edit code and run cells, you need to run the notebook yourself. Where would you like to run the notebook?

In the cloud (experimental)

Binder is a free, open source service that runs scientific notebooks in the cloud! It will take a while, usually 2-7 minutes to get a session.

On your computer

(Recommended if you want to store your changes.)

  1. Copy the notebook URL:
  2. Run Pluto

    (Also see: How to install Julia and Pluto)

  3. Paste URL in the Open box

Frontmatter

If you are publishing this notebook on the web, you can set the parameters below to provide HTML metadata. This is useful for search engines and social media.

Author 1

homework 10, version 0

👀 Reading hidden code
2.1 ms

Submission by: Jazzy Doe (jazz@mit.edu)

👀 Reading hidden code
9.3 ms

Homework 10: Climate modeling II

18.S191, fall 2020

👀 Reading hidden code
3.1 ms
👀 Reading hidden code
# edit the code below to set your name and kerberos ID (i.e. email without @mit.edu)

student = (name = "Jazzy Doe", kerberos_id = "jazz")

# you might need to wait until all other cells in this notebook have completed running.
# scroll around the page to see what's up
14.0 μs

Let's create a package environment:

👀 Reading hidden code
241 μs
👀 Reading hidden code
begin
import Pkg
Pkg.activate(mktempdir())
Pkg.add([
"Plots",
"PlutoUI",
"Images",
"FileIO",
"ImageMagick",
"ImageIO",
"OffsetArrays",
"PaddedViews",
"ThreadsX",
"BenchmarkTools",
])
using Statistics
using Plots
using PlutoUI
using Images
using OffsetArrays
using PaddedViews
using ThreadsX
using BenchmarkTools
end
❔
  Activating new project at `/tmp/jl_VhqMdx`
    Updating registry at `~/.julia/registries/General.toml`
   Resolving package versions...
    Updating `/tmp/jl_VhqMdx/Project.toml`
  [6e4b80f9] + BenchmarkTools v1.6.0
  [5789e2e9] + FileIO v1.17.0
  [82e4d734] + ImageIO v0.6.9
  [6218d12a] + ImageMagick v1.4.2
  [916415d5] + Images v0.26.2
  [6fe1bfb0] + OffsetArrays v1.17.0
  [5432bcbf] + PaddedViews v0.5.12
  [91a5bcdd] + Plots v1.40.14
  [7f904dfe] + PlutoUI v0.7.64
  [ac1d9e8a] + ThreadsX v0.1.12
    Updating `/tmp/jl_VhqMdx/Manifest.toml`
  [621f4979] + AbstractFFTs v1.5.0
  [6e696c72] + AbstractPlutoDingetjes v1.3.2
  [7d9f7c33] + Accessors v0.1.41
  [79e6a3ab] + Adapt v3.7.2
  [66dad0bd] + AliasTables v1.1.3
  [dce04be8] + ArgCheck v2.4.0
  [ec485272] + ArnoldiMethod v0.4.0
  [4fba245c] + ArrayInterface v7.5.1
  [13072b0f] + AxisAlgorithms v1.1.0
  [39de3d68] + AxisArrays v0.4.7
  [198e06fe] + BangBang v0.4.3
  [9718e550] + Baselet v0.1.1
  [6e4b80f9] + BenchmarkTools v1.6.0
  [d1d4a3ce] + BitFlags v0.1.9
  [62783981] + BitTwiddlingConvenienceFunctions v0.1.6
  [fa961155] + CEnum v0.5.0
  [2a0fbf3d] + CPUSummary v0.2.6
  [aafaddc9] + CatIndices v0.2.2
  [d360d2e6] + ChainRulesCore v1.25.1
  [9e997f8a] + ChangesOfVariables v0.1.10
  [fb6a15b2] + CloseOpenIntervals v0.1.13
  [aaaa29a8] + Clustering v0.15.8
  [944b1d66] + CodecZlib v0.7.8
  [35d6a980] + ColorSchemes v3.29.0
  [3da002f7] + ColorTypes v0.12.1
  [c3611d14] + ColorVectorSpace v0.11.0
  [5ae59095] + Colors v0.13.1
  [bbf7d656] + CommonSubexpressions v0.3.1
  [34da2185] + Compat v4.16.0
  [a33af91c] + CompositionsBase v0.1.2
  [ed09eef8] + ComputationalResources v0.3.2
  [f0e56b4a] + ConcurrentUtilities v2.5.0
  [187b0558] + ConstructionBase v1.5.8
  [d38c429a] + Contour v0.6.3
  [150eb455] + CoordinateTransformations v0.6.3
  [adafc99b] + CpuId v0.3.1
  [dc8bdbbb] + CustomUnitRanges v1.0.2
  [9a962f9c] + DataAPI v1.16.0
  [864edb3b] + DataStructures v0.18.22
  [e2d170a0] + DataValueInterfaces v1.0.0
  [244e2a9f] + DefineSingletons v0.1.2
  [163ba53b] + DiffResults v1.1.0
  [b552c78f] + DiffRules v1.15.1
  [b4f34e82] + Distances v0.10.12
  [ffbed154] + DocStringExtensions v0.9.5
  [460bff9d] + ExceptionUnwrapping v0.1.11
  [c87230d0] + FFMPEG v0.4.2
  [4f61f5a4] + FFTViews v0.3.2
  [7a1cc6ca] + FFTW v1.9.0
  [5789e2e9] + FileIO v1.17.0
  [53c48c17] + FixedPointNumbers v0.8.5
  [1fa38f19] + Format v1.3.7
  [f6369f11] + ForwardDiff v1.0.1
  [28b8d3ca] + GR v0.73.6
  [a2bd30eb] + Graphics v1.1.3
  [86223c79] + Graphs v1.12.1
  [42e2da0e] + Grisu v1.0.2
  [cd3eb016] + HTTP v1.10.16
  [2c695a8d] + HistogramThresholding v0.3.1
  [3e5b6fbb] + HostCPUFeatures v0.1.17
  [47d2ed2b] + Hyperscript v0.0.5
  [ac1192a8] + HypertextLiteral v0.9.5
  [b5f81e59] + IOCapture v0.2.5
  [615f187c] + IfElse v0.1.1
  [2803e5a7] + ImageAxes v0.6.12
  [c817782e] + ImageBase v0.1.7
  [cbc4b850] + ImageBinarization v0.3.1
  [f332f351] + ImageContrastAdjustment v0.3.12
  [a09fc81d] + ImageCore v0.10.5
  [89d5987c] + ImageCorners v0.1.3
  [51556ac3] + ImageDistances v0.2.17
  [6a3955dd] + ImageFiltering v0.7.10
  [82e4d734] + ImageIO v0.6.9
  [6218d12a] + ImageMagick v1.4.2
  [bc367c6b] + ImageMetadata v0.9.10
  [787d08f9] + ImageMorphology v0.4.6
  [2996bd0c] + ImageQualityIndexes v0.3.7
  [80713f31] + ImageSegmentation v1.9.0
  [4e3cecfd] + ImageShow v0.3.8
  [02fcd773] + ImageTransformations v0.10.2
  [916415d5] + Images v0.26.2
  [9b13fd28] + IndirectArrays v1.0.0
  [d25df0c9] + Inflate v0.1.5
  [22cec73e] + InitialValues v0.3.1
  [1d092043] + IntegralArrays v0.1.6
  [a98d9a8b] + Interpolations v0.15.1
  [8197267c] + IntervalSets v0.7.11
  [3587e190] + InverseFunctions v0.1.17
  [92d709cd] + IrrationalConstants v0.2.4
  [c8e1da08] + IterTools v1.4.0
  [82899510] + IteratorInterfaceExtensions v1.0.0
  [033835bb] + JLD2 v0.5.12
  [1019f520] + JLFzf v0.1.11
  [692b3bcd] + JLLWrappers v1.7.0
  [682c06a0] + JSON v0.21.4
  [b835a17e] + JpegTurbo v0.1.6
  [b964fa9f] + LaTeXStrings v1.4.0
  [23fbe1c1] + Latexify v0.16.8
  [10f19ff3] + LayoutPointers v0.1.17
  [8cdb02fc] + LazyModules v0.3.1
  [2ab3a3ac] + LogExpFunctions v0.3.28
  [e6f89c97] + LoggingExtras v1.1.0
  [bdcacae8] + LoopVectorization v0.12.172
  [6c6e2e6c] + MIMEs v1.1.0
  [1914dd2f] + MacroTools v0.5.16
  [d125e4d3] + ManualMemory v0.1.8
  [dbb5928d] + MappedArrays v0.4.2
  [739be429] + MbedTLS v1.1.9
  [442fdcdd] + Measures v0.3.2
  [626554b9] + MetaGraphs v0.8.0
  [128add7d] + MicroCollections v0.2.0
  [e1d29d7a] + Missings v1.2.0
  [e94cdb99] + MosaicViews v0.3.4
  [77ba4419] + NaNMath v1.0.3
  [b8a86587] + NearestNeighbors v0.4.21
  [f09324ee] + Netpbm v1.1.1
  [6fe1bfb0] + OffsetArrays v1.17.0
  [52e1d378] + OpenEXR v0.3.3
  [4d8831e6] + OpenSSL v1.5.0
  [bac558e1] + OrderedCollections v1.8.1
  [f57f5aa1] + PNGFiles v0.4.4
  [5432bcbf] + PaddedViews v0.5.12
  [d96e819e] + Parameters v0.12.3
  [69de0a69] + Parsers v2.8.3
  [eebad327] + PkgVersion v0.3.3
  [ccf2f8ad] + PlotThemes v3.3.0
  [995b91a9] + PlotUtils v1.4.3
  [91a5bcdd] + Plots v1.40.14
  [7f904dfe] + PlutoUI v0.7.64
  [1d0040c9] + PolyesterWeave v0.2.2
  [f27b6e38] + Polynomials v4.0.19
  [aea7be01] + PrecompileTools v1.2.1
  [21216c6a] + Preferences v1.4.3
  [92933f4c] + ProgressMeter v1.10.4
  [43287f4e] + PtrArrays v1.3.0
  [4b34888f] + QOI v1.0.1
  [94ee1d12] + Quaternions v0.7.6
  [b3c3ace0] + RangeArrays v0.3.2
  [c84ed2f1] + Ratios v0.4.5
  [c1ae055f] + RealDot v0.1.0
  [3cdcf5f2] + RecipesBase v1.3.4
  [01d81517] + RecipesPipeline v0.6.12
  [189a3867] + Reexport v1.2.2
  [42d2dcc6] + Referenceables v0.1.3
  [dee08c22] + RegionTrees v0.3.2
  [05181044] + RelocatableFolders v1.0.1
  [ae029012] + Requires v1.3.1
  [6038ab10] + Rotations v1.7.1
  [fdea26ae] + SIMD v3.7.1
  [94e857df] + SIMDTypes v0.1.0
  [476501e8] + SLEEFPirates v0.6.43
  [6c6a2e73] + Scratch v1.2.1
  [efcf1570] + Setfield v1.1.2
  [992d4aef] + Showoff v1.0.3
  [777ac1f9] + SimpleBufferStream v1.2.0
  [699a6c99] + SimpleTraits v0.9.4
  [47aef6b3] + SimpleWeightedGraphs v1.5.0
  [45858cf5] + Sixel v0.1.3
  [a2af1166] + SortingAlgorithms v1.2.1
  [276daf66] + SpecialFunctions v2.5.1
  [171d559e] + SplittablesBase v0.1.15
  [860ef19b] + StableRNGs v1.0.3
  [cae243ae] + StackViews v0.1.2
  [aedffcd0] + Static v0.8.9
  [0d7ed370] + StaticArrayInterface v1.6.0
  [90137ffa] + StaticArrays v1.9.13
  [1e83bf80] + StaticArraysCore v1.4.3
  [82ae8749] + StatsAPI v1.7.1
  [2913bbd2] + StatsBase v0.34.4
  [3783bdb8] + TableTraits v1.0.1
  [bd369af6] + Tables v1.12.1
  [62fd8b95] + TensorCore v0.1.1
  [8290d209] + ThreadingUtilities v0.5.5
  [ac1d9e8a] + ThreadsX v0.1.12
  [731e570b] + TiffImages v0.11.4
  [06e1c1a7] + TiledIteration v0.5.0
  [3bb67fe8] + TranscodingStreams v0.11.3
  [28d57a85] + Transducers v0.4.84
  [410a4b4d] + Tricks v0.1.10
  [5c2747f8] + URIs v1.5.2
  [3a884ed6] + UnPack v1.0.2
  [1cfade01] + UnicodeFun v0.4.1
  [1986cc42] + Unitful v1.23.1
  [45397f5d] + UnitfulLatexify v1.7.0
  [41fe7b60] + Unzip v0.2.0
  [3d5dd08c] + VectorizationBase v0.21.71
  [e3aaa7dc] + WebP v0.1.3
  [efce3f68] + WoodburyMatrices v1.0.0
  [6e34b625] + Bzip2_jll v1.0.9+0
  [83423d85] + Cairo_jll v1.18.5+0
  [ee1fde0b] + Dbus_jll v1.16.2+0
  [2702e6a9] + EpollShim_jll v0.0.20230411+1
  [2e619515] + Expat_jll v2.6.5+0
  [b22a6f82] + FFMPEG_jll v4.4.4+1
  [f5851436] + FFTW_jll v3.3.11+0
  [a3f928ae] + Fontconfig_jll v2.16.0+0
  [d7e528f0] + FreeType2_jll v2.13.4+0
  [559328eb] + FriBidi_jll v1.0.17+0
  [0656b61e] + GLFW_jll v3.4.0+2
  [d2c73de3] + GR_jll v0.73.6+0
  [78b55507] + Gettext_jll v0.21.0+0
  [61579ee1] + Ghostscript_jll v9.55.0+4
  [59f7168a] + Giflib_jll v5.2.3+0
  [7746bdde] + Glib_jll v2.84.0+0
  [3b182d85] + Graphite2_jll v1.3.15+0
  [2e76f6c2] + HarfBuzz_jll v8.5.1+0
  [c73af94c] + ImageMagick_jll v7.1.1048+0
  [905a6f67] + Imath_jll v3.1.11+0
  [1d5cc7b8] + IntelOpenMP_jll v2025.0.4+0
  [aacddb02] + JpegTurbo_jll v3.1.1+0
  [c1c5ebd0] + LAME_jll v3.100.2+0
  [88015f11] + LERC_jll v3.0.0+1
  [1d63c593] + LLVMOpenMP_jll v18.1.8+0
  [dd4b983a] + LZO_jll v2.10.3+0
  [e9f186c6] + Libffi_jll v3.4.7+0
  [7e76a0d4] + Libglvnd_jll v1.7.1+1
  [94ce4f54] + Libiconv_jll v1.18.0+0
  [4b2f31a3] + Libmount_jll v2.41.0+0
  [89763e89] + Libtiff_jll v4.5.1+1
  [38a345b3] + Libuuid_jll v2.41.0+0
  [d3a379c0] + LittleCMS_jll v2.16.0+0
  [856f044c] + MKL_jll v2025.0.1+1
  [e7412a2a] + Ogg_jll v1.3.5+1
  [18a262bb] + OpenEXR_jll v3.2.4+0
  [643b3616] + OpenJpeg_jll v2.5.4+0
  [458c3c95] + OpenSSL_jll v3.5.0+0
  [efe28fd5] + OpenSpecFun_jll v0.5.6+0
  [91d4177d] + Opus_jll v1.3.3+0
  [36c8627f] + Pango_jll v1.56.3+0
  [30392449] + Pixman_jll v0.44.2+0
  [c0090381] + Qt6Base_jll v6.7.1+1
  [a44049a8] + Vulkan_Loader_jll v1.3.243+0
  [a2964d1f] + Wayland_jll v1.23.1+0
  [2381bf8a] + Wayland_protocols_jll v1.44.0+0
  [02c8fc9c] + XML2_jll v2.13.6+1
  [ffd25f8a] + XZ_jll v5.8.1+0
  [f67eecfb] + Xorg_libICE_jll v1.1.2+0
  [c834827a] + Xorg_libSM_jll v1.2.6+0
  [4f6342f7] + Xorg_libX11_jll v1.8.12+0
  [0c0b7dd1] + Xorg_libXau_jll v1.0.13+0
  [935fb764] + Xorg_libXcursor_jll v1.2.4+0
  [a3789734] + Xorg_libXdmcp_jll v1.1.6+0
  [1082639a] + Xorg_libXext_jll v1.3.7+0
  [d091e8ba] + Xorg_libXfixes_jll v6.0.1+0
  [a51aa0fd] + Xorg_libXi_jll v1.8.3+0
  [d1454406] + Xorg_libXinerama_jll v1.1.6+0
  [ec84b674] + Xorg_libXrandr_jll v1.5.5+0
  [ea2f1a96] + Xorg_libXrender_jll v0.9.12+0
  [c7cfdc94] + Xorg_libxcb_jll v1.17.1+0
  [cc61e674] + Xorg_libxkbfile_jll v1.1.3+0
  [e920d4aa] + Xorg_xcb_util_cursor_jll v0.1.4+0
  [12413925] + Xorg_xcb_util_image_jll v0.4.1+0
  [2def613f] + Xorg_xcb_util_jll v0.4.1+0
  [975044d2] + Xorg_xcb_util_keysyms_jll v0.4.1+0
  [0d47668e] + Xorg_xcb_util_renderutil_jll v0.3.10+0
  [c22f9ab0] + Xorg_xcb_util_wm_jll v0.4.2+0
  [35661453] + Xorg_xkbcomp_jll v1.4.7+0
  [33bec58e] + Xorg_xkeyboard_config_jll v2.44.0+0
  [c5fb5394] + Xorg_xtrans_jll v1.6.0+0
  [3161d3a3] + Zstd_jll v1.5.7+1
  [35ca27e7] + eudev_jll v3.2.14+0
  [214eeab7] + fzf_jll v0.61.1+0
  [a4ae2306] + libaom_jll v3.11.0+0
  [0ac62f75] + libass_jll v0.15.2+0
  [1183f4f0] + libdecor_jll v0.2.2+0
  [2db6ffa8] + libevdev_jll v1.13.4+0
  [f638f0a6] + libfdk_aac_jll v2.0.3+0
  [36db933b] + libinput_jll v1.28.1+0
  [b53b4c65] + libpng_jll v1.6.49+0
  [075b6546] + libsixel_jll v1.10.5+0
  [f27f6e37] + libvorbis_jll v1.3.7+2
  [c5f90fcd] + libwebp_jll v1.4.0+0
  [009596ad] + mtdev_jll v1.1.7+0
  [1317d2d5] + oneTBB_jll v2022.0.0+0
  [1270edf5] + x264_jll v2021.5.5+0
  [dfaa095f] + x265_jll v3.5.0+0
  [d8fb68d0] + xkbcommon_jll v1.8.1+0
  [0dad84c5] + ArgTools
  [56f22d72] + Artifacts
  [2a0f44e3] + Base64
  [ade2ca70] + Dates
  [8bb1440f] + DelimitedFiles
  [8ba89e20] + Distributed
  [f43a241f] + Downloads
  [7b1f6079] + FileWatching
  [9fa8497b] + Future
  [b77e0a4c] + InteractiveUtils
  [4af54fe1] + LazyArtifacts
  [b27032c2] + LibCURL
  [76f85450] + LibGit2
  [8f399da3] + Libdl
  [37e2e46d] + LinearAlgebra
  [56ddb016] + Logging
  [d6f4376e] + Markdown
  [a63ad114] + Mmap
  [ca575930] + NetworkOptions
  [44cfe95a] + Pkg
  [de0858da] + Printf
  [9abbd945] + Profile
  [3fa0cd96] + REPL
  [9a3f8284] + Random
  [ea8e919c] + SHA
  [9e88b42a] + Serialization
  [1a1011a3] + SharedArrays
  [6462fe0b] + Sockets
  [2f01184e] + SparseArrays
  [10745b16] + Statistics
  [4607b0f0] + SuiteSparse
  [fa267f1f] + TOML
  [a4e569a6] + Tar
  [8dfed614] + Test
  [cf7118a7] + UUIDs
  [4ec0a83e] + Unicode
  [e66e0078] + CompilerSupportLibraries_jll
  [deac9b47] + LibCURL_jll
  [29816b5a] + LibSSH2_jll
  [c8ffd9c3] + MbedTLS_jll
  [14a3606d] + MozillaCACerts_jll
  [4536629a] + OpenBLAS_jll
  [05823500] + OpenLibm_jll
  [efcefdf7] + PCRE2_jll
  [83775a58] + Zlib_jll
  [8e850b90] + libblastrampoline_jll
  [8e850ede] + nghttp2_jll
  [3f19e933] + p7zip_jll
17.8 s
👀 Reading hidden code
120 μs

In Lecture 23 (video above), we looked at a 2D ocean model that included two physical processes: advection (flow of heat) and diffusion (spreading of heat). This homework includes the model from the lecture, and you will be able to experiment with it yourself!

The model is written in a way that it can be extended with more physical processes. In this homework we will add two more effects, introduced in the Energy Balance Model from our last homework: absorbed and emitted radiation.

👀 Reading hidden code
27.6 ms

Exercise 1 - Advection-diffusion

Included below is the two-dimensional advection-diffusion model from Lecture 23. To keep this homework concise, we have only included the code. To see the original notebook with more detailed comments, use the link below:

👀 Reading hidden code
5.1 ms

Click here to download and run the Lecture 23 notebook in a new tab.

👀 Reading hidden code
30.8 μs

Advection & diffusion

Notice that both functions have a main method with the following signature:

(::Array{Float64,2}, ::ClimateModel) maps to ::Array{Float64,2}.

As we will see later, ClimateModel contains the grid, the velocity vector field and the simulation parameters.

👀 Reading hidden code
5.1 ms
advect (generic function with 3 methods)
begin
# main method:
advect(T::Array{Float64,2}, O::ClimateModel) =
advect(T, O.u, O.v, O.grid.Δy, O.grid.Δx)
# helper methods:
advect(T::Array{Float64,2}, u, v, Δy, Δx) = pad_zeros([
advect(T, u, v, Δy, Δx, j, i)
for j=2:size(T, 1)-1, i=2:size(T, 2)-1
])
advect(T::Array{Float64,2}, u, v, Δy, Δx, j, i) = .-(
u[j, i].*sum(xgrad_kernel[0, -1:1].*T[j, i-1:i+1])/(2Δx) .+
v[j, i].*sum(ygrad_kernel[-1:1, 0].*T[j-1:j+1, i])/(2Δy)
)
end
👀 Reading hidden code
2.8 ms
xgrad_kernel = OffsetArray(reshape([-1., 0, 1.], 1, 3), 0:0, -1:1);
👀 Reading hidden code
28.8 ms
ygrad_kernel = OffsetArray(reshape([-1., 0, 1.], 3, 1), -1:1, 0:0);
👀 Reading hidden code
34.9 μs
diffuse (generic function with 3 methods)
begin
# main method:
diffuse(T::Array{Float64,2}, O::ClimateModel) =
diffuse(T, O.params.κ, O.grid.Δy, O.grid.Δx)
# helper methods:
diffuse(T, κ, Δy, Δx) = pad_zeros([
diffuse(T, κ, Δy, Δx, j, i) for j=2:size(T, 1)-1, i=2:size(T, 2)-1
])
diffuse(T, κ, Δy, Δx, j, i) = κ.*(
sum(xdiff_kernel[0, -1:1].*T[j, i-1:i+1])/(Δx^2) +
sum(ydiff_kernel[-1:1, 0].*T[j-1:j+1, i])/(Δy^2)
)
end
👀 Reading hidden code
2.7 ms
xdiff_kernel = OffsetArray(reshape([1., -2., 1.], 1, 3), 0:0, -1:1);
👀 Reading hidden code
23.2 μs
ydiff_kernel = OffsetArray(reshape([1., -2., 1.], 3, 1), -1:1, 0:0);
👀 Reading hidden code
22.6 μs
pad_zeros

Surround an array with one layer of zeros.

👀 Reading hidden code
1.3 ms

Data structures

Let's look at our first type, Grid. Notice that it only has one 'constructor function', which takes N (number of longitudinal grid points) and L (longitudinal size in meters) as arguments. The struct contains more fields, these are precomputed and stored for performance.

👀 Reading hidden code
292 μs
begin
struct Grid
N::Int64
L::Float64

Δx::Float64
Δy::Float64

x::Array{Float64, 2}
y::Array{Float64, 2}

Nx::Int64
Ny::Int64

# constructor function:
function Grid(N, L)
Δx = L/N # [m]
Δy = L/N # [m]

x = 0. -Δx/2.:Δx:L +Δx/2.
x = reshape(x, (1, size(x,1)))
y = -L -Δy/2.:Δy:L +Δy/2.
y = reshape(y, (size(y,1), 1))

Nx, Ny = size(x, 2), size(y, 1)

return new(N, L, Δx, Δy, x, y, Nx, Ny)
end
end

Base.zeros(G::Grid) = zeros(G.Ny, G.Nx)
end
👀 Reading hidden code
2.6 ms
Grid(5,300.0e3)
👀 Reading hidden code
12.8 μs

Next, let's look at three types.

Two structs: OceanModel and OceanModelParameters, and an abstract type: ClimateModel.

👀 Reading hidden code
259 μs
OceanModelParameters
Base.@kwdef struct OceanModelParameters
κ::Float64=1.e4
end
👀 Reading hidden code
2.9 ms
abstract type ClimateModel end
👀 Reading hidden code
330 μs
begin
struct OceanModel <: ClimateModel
grid::Grid
params::OceanModelParameters

u::Array{Float64, 2}
v::Array{Float64, 2}
end

OceanModel(G::Grid, params::OceanModelParameters) =
OceanModel(G, params, zeros(G), zeros(G))
OceanModel(G::Grid) =
OceanModel(G, OceanModelParameters(), zeros(G), zeros(G))
end;
👀 Reading hidden code
2.3 ms
true
OceanModel <: ClimateModel # it's a subtype!
👀 Reading hidden code
10.4 μs

Timestepping

The OceanModel struct contains a complete description of the model being simulated. The next struct, ClimateModelSimulation, contains a model, together with the simulation results. It is mutable: timestepping the model means modifying a ClimateModelSimulation object.

👀 Reading hidden code
330 μs
ClimateModelSimulation
begin
mutable struct ClimateModelSimulation{ModelType<:ClimateModel}
model::ModelType
T::Array{Float64, 2}
Δt::Float64
iteration::Int64
end
ClimateModelSimulation(C::ModelType, T, Δt) where ModelType =
ClimateModelSimulation{ModelType}(C, T, Δt, 0)
end
👀 Reading hidden code
3.6 ms
function timestep!(sim::ClimateModelSimulation{OceanModel})
update_ghostcells!(sim.T)
tendencies = advect(sim.T, sim.model) .+ diffuse(sim.T, sim.model)
sim.T .+= sim.Δt*tendencies
sim.iteration += 1
end;
👀 Reading hidden code
861 μs
update_ghostcells! (generic function with 1 method)
function update_ghostcells!(A::Array{Float64,2}; option="no-flux")
# Atmp = @view A[:,:]
if option=="no-flux"
A[1, :] .= A[2, :]; A[end, :] .= A[end-1, :]
A[:, 1] .= A[:, 2]; A[:, end] .= A[:, end-1]
end
end
👀 Reading hidden code
1.8 ms

Exercise 1.1 - Running the model

In the next few cells, we set up a simulation. We have included an interactive visualisation of the simulation.

👉 Familiarize yourself with the simulation through interaction. Get a sense for each parameter by changing their values.

👀 Reading hidden code
326 μs
default_grid = Grid(10, 6000.0e3);
👀 Reading hidden code
16.6 μs

Uncomment (Ctrl+/ or Cmd+/) one of the lines below to choose between the different velocity fields:

👀 Reading hidden code
220 μs
# ocean_velocities = zeros(default_grid), zeros(default_grid);
ocean_velocities = PointVortex(default_grid, Ω=0.5);
# ocean_velocities = DoubleGyre(default_grid);
👀 Reading hidden code
1.2 s

Choose the initial temperature state:

👀 Reading hidden code
181 μs
# ocean_T_init = InitBox(default_grid; value=50);
ocean_T_init = InitBox(default_grid, value=50, xspan=true);
# ocean_T_init = linearT(default_grid);
👀 Reading hidden code
108 ms

We define our ocean simulation. Run this cell again to reset the simulation to the initial state.

👀 Reading hidden code
192 μs
ocean_sim = let
P = OceanModelParameters(κ=κ_ex)
u, v = ocean_velocities
model = OceanModel(default_grid, P, u*2. ^U_ex, v*2. ^U_ex)
Δt = 12*60*60
ClimateModelSimulation(model, copy(ocean_T_init), Δt)
end;
👀 Reading hidden code
15.5 ms

Simulation controls

👀 Reading hidden code
227 μs

Click to show the velocity field or to show temperature anomalies instead of absolute values

👀 Reading hidden code
215 ms

Vary the current speed U: 1.0 [× reference]

👀 Reading hidden code
4.3 ms
👀 Reading hidden code
148 ms

Vary the diffusivity κ: 10000.0 [m²/s]

👀 Reading hidden code
122 ms
image/svg+xml image/svg+xml image/svg+xml speed:
👀 Reading hidden code
40.6 ms
👀 Reading hidden code
1.8 s
Velocity field for a single circular vortex
👀 Reading hidden code
174 μs
PointVortex (generic function with 1 method)
👀 Reading hidden code
4.1 ms
diagnose_velocities (generic function with 1 method)
👀 Reading hidden code
3.0 ms
impose_no_flux! (generic function with 1 method)
👀 Reading hidden code
955 μs
Quasi-realistic ocean velocity field u=(u,v)

Our velocity field is given by an analytical solution to the classic wind-driven gyre problem, which is given by solving the fourth-order partial differential equation:

ϵM^4Ψ^+Ψ^x^=×τ^z,

where the hats denote that all of the variables have been non-dimensionalized and all of their constant coefficients have been bundles into the single parameter ϵMνβL3.

The solution makes use of an advanced asymptotic method (valid in the limit that ϵ1) known as boundary layer analysis (see MIT course 18.305 to learn more).

👀 Reading hidden code
11.0 ms
DoubleGyre (generic function with 1 method)
👀 Reading hidden code
5.0 ms
Some simple initial temperature fields
👀 Reading hidden code
199 μs
constantT (generic function with 1 method)
👀 Reading hidden code
1.4 ms
linearT (generic function with 1 method)
👀 Reading hidden code
2.2 ms
InitBox (generic function with 1 method)
👀 Reading hidden code
2.6 ms
plot_kernel (generic function with 1 method)
👀 Reading hidden code
955 μs

👉 Some parameters have a physical meaning (κ, u and v), while other parameters control our numerical process. Choose two of these numerical parameters, and describe their effect on the simulation's runtime and the simulation's accuracy.

👀 Reading hidden code
277 μs

Hello!

numerical_parameters_observation = md"""

Hello!
"""
👀 Reading hidden code
243 μs

Exercise 2 - Complexity

In this class we have restricted ourself to small problems (Nt<100 timesteps and Nx;y<30 spatial grid-cells) so that they can be run interactively on an average computer. In state-of-the-art climate modelling however, the goal is to push the numerical resolution N to be as large as possible (the grid spacing Δt or Δx as small as possible), to resolve physical processes that improve the realism of the simulation (see below).

👀 Reading hidden code
437 μs
👀 Reading hidden code
117 μs

Here, we provide a simple estimate of the computational complexity of climate models, which reveals a substantial challenge to the improvement of climate models.

Our climate model algorithm can be summarized by the recursive formula:

Ti,jn+1=Ti,jn+Δt(tendencies)

for each time step n{1,,M}, and for each grid point i{1,,Nx},j{1,,Ny}.

Our goal is to simulate an ocean of fixed size (e.g. 6000 km), for a fixed amount of time (e.g. 100 years). By choosing smaller Δt, Δx and Δy, we get a more accurate simulation, but for the same size, it requires more steps. i.e.

M=O(Δt1)Nx=O(Δx1)Ny=O(Δy1).

Now, the total runtime of our simulation is proportional to the number of steps we need to take, which is MNxNy. For a fixed aspect ratio Ny=2Nx, we get

runtime=O(M)O(2Nx2)=O(M)O(Nx2).

Exercise 2.1

For constant M, we want to verify that runtime=O(Nx2) holds for our numerical model.

👉 Write a function model_runtime that takes N as an argument, and sets up a model with grid of resolution N, and returns the runtime of a single timestep!.

👀 Reading hidden code
716 μs
# function runtime(N)
# return missing
# end
👀 Reading hidden code
15.0 μs
runtime (generic function with 1 method)
function runtime(N)
G = Grid(N, 6.e6);
P = OceanModelParameters(1.e4);

#u, v = DoubleGyre(G)
#u, v = PointVortex(G, Ω=0.5)
u, v = zeros(G), zeros(G)

model = OceanModel(G, P, u, v)

IC = InitBox(G)
#IC = InitBox(G, nx=G.Nx÷2-1)
#IC = linearT(G)

Δt = 6*60*60
S = ClimateModelSimulation(model, copy(IC), Δt)

return @elapsed timestep!(S)
end
👀 Reading hidden code
4.0 ms

Hint

To measure the runtime of a Julia command, you can do:

runtime = @elapsed do_something()

To get a more precise benchmark, you can average a fixed number of runs, by putting @elapsed in front of a for loop, for example.

👀 Reading hidden code
44.2 ms

👉 Call your runtime function on a range of values for N, and use a plot to demonstrate that the predicted runtime complexity holds.

👀 Reading hidden code
199 μs

👀 Reading hidden code
67.6 μs
8:8:200
Nvec = 8:8:200
👀 Reading hidden code
12.2 μs
tvec = runtime.(Nvec)
👀 Reading hidden code
4.9 s
begin
plot(Nvec, tvec, xlabel="Number of Grid Cells (in x-direction)", ylabel="elapsed time per timestep [s]")
end |> as_svg
👀 Reading hidden code
113 ms

Exercise 2.2 - The CFL condition on Δt

In Exercise 1, look for the definition of Δt. It is currently set to 12*60*60 (12 hours).

👉 Double Δt and run the simulation again. You should see that it runs faster, great! Now, keep doubling Δt until you see something 'strange'. Describe what you see.

👀 Reading hidden code
338 μs

Hi!

Δt_doubling_observations = md"""

Hi!

"""
👀 Reading hidden code
228 μs

What you experienced is a numerical instability of the discretization method in our simulation. This is not caused by floating point errors – it is a theoretical limitation of our method.

To ensure the stability of our finite-difference approximation for advection, heat should not be displaced more than one grid cell in a single timestep. Mathematically, we can ensure this by checking that the distance LCFLmax(|u|)Δt is significantly less than the width Δx=Δy of a single grid cell:

LCFLmax(|u|)ΔtΔx

or

ΔtΔxmax(|u|),

which is known as the Courant-Freidrichs-Levy (CFL) condition.

The exact meaning of here depends on the simulation at hand, but in our case, it means that there is at least an order of magnitude difference:

Δt<0.1Δxmax(|u|).

Given below is a function CFL_advection that takes a ClimateModel and computes the CFL value: Δx/max(|u|).

👀 Reading hidden code
90.9 ms
CFL_advection (generic function with 1 method)
function CFL_advection(model::ClimateModel)
model.grid.Δx / maximum(sqrt.(model.u.^2 + model.v.^2))
end
👀 Reading hidden code
705 μs
ocean_sim.Δt, 0.1 * CFL_advection(ocean_sim.model)
👀 Reading hidden code
27.3 μs

👉 Using the interactive simulation of Exercise 1, verify that the CFL condition is (somewhat) true. Increase the magnitude of u until you start to see numerical artefacts. Now, halve the value fo Δt, and again, increase the magnitude of u. You should find that halving Δt allows for twice the velocity magnitude. The same link exists between Δt and Δx.

👀 Reading hidden code
285 μs

The CFL inequality states that if we want to decrease the grid spacing Δx (or increase the resolution Nx), we also have to decrease the timestep Δt by the same factor. In other words, the timestep can not be thought of as fixed – it depends on the spatial resolution: ΔtΔt0Δx. This means that M=O(Nx).

Revisiting our complexity equation, we now have

runtime=O(M)O(Nx2)=O(Nx3).

In other words, in a 2-D model, 2x the spatial resolution requires 8x the computational power.

👀 Reading hidden code
400 μs

Exercise 2.3 - Moore's Law

In practice, state-of-the-art climate models are 3-D, not 2-D. It turns out that to preserve the aspect ratio of oceanic motions, the vertical grid resolution should also be increased NzNx, such that in reality the computational complexity of climate models is:

runtime=O(Nx4).

This is the fundamental challenge of high-performance climate computing: to increase the resolution of the models by a factor of 2, the model's run-time increases by a factor of 24=16.

The figure below shows how the grid spacing of state-of-the-art climate models has decreased from 500 km in 1990 (FAR) to 100 km in the 2010s (AR4). In other words, grid resolution increased by a factor of 5 in 20 years.

👀 Reading hidden code
558 μs
👀 Reading hidden code
132 μs

Moore's law is the observation that the number of transistors in a dense integrated circuit doubles about every two years. In the context of climate modelling, we can interpret this as meaning that the computational complexity allowed by our best high-performance computers C doubles every two years:

C(t)=C(2020)2(t2020)/2

Present-day simulations have a grid spacing of Δx=30 km (or about Nx=L/Δx20000 km30 km700).

👉 By extrapolating Moore's law forward into the future, estimate how long it would take for Δx to reach to the 500 meter scale of clouds, one of the important climate processes (Nx=L/Δx40000).

👀 Reading hidden code
336 μs
missing
cloud_resolution_possible_at = let
missing
end
👀 Reading hidden code
15.8 μs

Exercise 3 - Radiation

In Homework 9, we used a zero-dimensional (0-D) Energy Balance Model (EBM) to understand how Earth's average radiative imbalance results in temperature changes:

CTt= +S(1α)4(absorbed radiation)(ABT)(outgoing radiation)

This week we will do the same, but now in two-dimensions (2-D), where in addition to heat being added or removed from the system by radiation, heat can be transported around the system by oceanic advection and diffusion (see also Lectures 22 & 23 for 1-D and 2-D advection-diffusion). The governing equation for temperature T(x,y,t) in our coupled climate model is:

Tt=+u(x,y)Tx+v(x,y)Ty(advection)+κ(2Tx2+2Ty2)(diffusion)+S(x,y)(1α(T))4C(absorbed radiation)(ABT)C.(outgoing radiation)

👀 Reading hidden code
590 μs
Parameters

Below we define two new types: RadiationOceanModel and RadiationOceanModelParameters. Notice the similarities and differences with our original model. By making RadiationOceanModel also a subtype of ClimateModel, we will be able to re-use much of our original code.

👀 Reading hidden code
255 μs
begin
struct RadiationOceanModel <: ClimateModel
grid::Grid
params::RadiationOceanModelParameters
u::Array{Float64, 2}
v::Array{Float64, 2}
end

RadiationOceanModel(G::Grid, P::RadiationOceanModelParameters, u, v) =
RadiationOceanModel(G, P, u, v)
RadiationOceanModel(G::Grid, P::RadiationOceanModelParameters) =
RadiationOceanModel(G, P, zeros(G), zeros(G))
RadiationOceanModel(G::Grid) =
RadiationOceanModel(G, RadiationOceanModelParameters(), zeros(G), zeros(G))
end;
👀 Reading hidden code
2.5 ms
RadiationOceanModelParameters
Base.@kwdef struct RadiationOceanModelParameters
κ::Float64=4.e4
C::Float64=51.0 * 60*60*24*365.25 # converted from [W*year/m^2/K] to [J/m^2/K]
A::Float64=210
B::Float64=-1.3
S_mean::Float64 = 1380
α0::Float64=0.3
αi::Float64=0.55
ΔT::Float64=2.0
end
👀 Reading hidden code
34.1 ms

Notice that this struct has Base.@kwdef in front of its definition. This allows us to:

  1. assign default values to the struct fields, directly inside the definition, and

  2. automatically create an easier constructor function that takes keyword arguments:

👀 Reading hidden code
8.5 ms
RadiationOceanModelParameters()
👀 Reading hidden code
9.5 μs
RadiationOceanModelParameters(S_mean=2000)
👀 Reading hidden code
8.5 ms

Exercise 3.1 - Absorbed radiation

The ocean absorbs solar radiation, increasing the temperature. Just like in our EBM model, we model the ocean as a surface with a temperature-dependent albedo: α(T). A high albedo means that more sunlight is reflected, and less is absorbed. We model the albedo function as:

👀 Reading hidden code
366 μs
👀 Reading hidden code
5.1 s
α (generic function with 1 method)
function α(T::Float64; α0, αi, ΔT)
if T < -ΔT
return αi
elseif -ΔT <= T < ΔT
return αi + (α0-αi)*(T+ΔT)/(2ΔT)
elseif ΔT <= T
return α0
end
end
👀 Reading hidden code
2.2 ms

An area of ocean below 0°C is covered in ice, which is more reflective, and therefore absorbs less solar radiation. In our EBM model, this positive feedback leads to a bifurcation: under the same external conditions, the climate system has multiple equilibria.

In this week's two-dimensional model, the factor α is also two-dimensional: instead of a global albedo, every grid cell has its own temperature, which determines its own albedo, α(T(x,y,t)). We can now have an ocean with warm and cold regions, which absorb different amounts of radiation. The same positive feedback can have a local effect.

Here is a second method for α that takes a 2D array T with the current ocean temperatures and a RadiationOceanModel, and returns the 2D array of albedos. We use the dot operator to apply α pointwise to T, also called broadcasting.

👀 Reading hidden code
572 μs
α (generic function with 2 methods)
function α(T::Array{Float64,2}, model::RadiationOceanModel)
α.(T; α0=model.params.α0, αi=model.params.αi, ΔT=model.params.ΔT)
end
👀 Reading hidden code
730 μs
Solar insolation

Our rectangular grid represents the North Atlantic Ocean, stretching from the equator (latitude = 0°) to the North Pole (latitude = 90°) in the latitudinal direction (y), and from the east coast of North America to the west coast of Europe in the longitudinal direction (x). In reality, climate models have to explicitly deal with the curvature of the Earth when constructing their model grids. Here, we will just treat the North Atlantic Ocean as a rectangle with roughly the correct dimensions.

Just like the albedo, every grid cell will have a local amount of solar insolation. In our model, we use the annual average at the latitude of a grid cell S(y)/4. This is given by: (source)

👀 Reading hidden code
440 μs
S_at (generic function with 1 method)
function S_at(y::Float64; grid::Grid, S_mean::Float64)
S_mean .* (1+0.5*cos(2 * y_to_lat(y; grid=grid)))
end
👀 Reading hidden code
2.0 ms
👀 Reading hidden code
524 ms

Note that latitude is the horizontal axis in this graph, not the vertical.

👀 Reading hidden code
214 μs
y_to_lat (generic function with 1 method)
👀 Reading hidden code
1.6 ms

👉 Write a method absorbed_solar_radiation that takes a 2D array T with the current ocean temperatures and a RadiationOceanModel, and returns the tendencies corresponding to absorbed radiation. This is the analogue of advect and diffuse.

👀 Reading hidden code
214 μs
absorbed_solar_radiation (generic function with 1 method)
function absorbed_solar_radiation(T::Array{Float64,2}, model::RadiationOceanModel)
absorption = 1.0 .- α(T, model)
S = S_at.(model.grid.y; grid=model.grid, S_mean=model.params.S_mean)
S .* absorption ./ 4 ./ model.params.C
end
👀 Reading hidden code
999 μs
# function absorbed_solar_radiation(T::Array{Float64,2}, model::RadiationOceanModel)
# return missing
# end
👀 Reading hidden code
15.2 μs

Exercise 3.2 - Outgoing radiation

Just like in our EBM from before, when our ocean heats up by absorbing solar radiation, it also emits radiation back to space. This outgoing thermal radiation is what allows the ocean to eventually come to an equilibrium. The difference here is that each individual grid cell of our model radiatives according to it's local temperature Ti,j.

👀 Reading hidden code
355 μs
outgoing_thermal_radiation (generic function with 1 method)
function outgoing_thermal_radiation(T; C, A, B)
(A .- B .* (T)) ./ C
end
👀 Reading hidden code
1.9 ms
👀 Reading hidden code
133 ms

👉 Write a method outgoing_termal_radiation that takes a 2D array T with the current ocean temperatures and a RadiationOceanModel, and returns the tendencies corresponding to outgoing radiation. This is the analogue of advect and diffuse.

👀 Reading hidden code
246 μs
outgoing_thermal_radiation (generic function with 2 methods)
function outgoing_thermal_radiation(T::Array{Float64,2}, model::RadiationOceanModel)
outgoing_thermal_radiation(T; A=model.params.A, B=model.params.B, C=model.params.C)
end
👀 Reading hidden code
681 μs
# function outgoing_thermal_radiation(T::Array{Float64,2}, model::RadiationOceanModel)
# return missing
# end
👀 Reading hidden code
15.1 μs

Exercise 3.3 - Running the model

Let's define a new timestep! method for our new model. This is exactly the same as our advection-diffusion model, with the addition of the two radiation tendencies.

👀 Reading hidden code
265 μs
timestep! (generic function with 2 methods)
function timestep!(sim::ClimateModelSimulation{RadiationOceanModel})
update_ghostcells!(sim.T)
tendencies =
advect(sim.T, sim.model) .+
diffuse(sim.T, sim.model) .+
absorbed_solar_radiation(sim.T, sim.model) .-
outgoing_thermal_radiation(sim.T, sim.model)
sim.T .+= sim.Δt*tendencies
sim.iteration += 1
end
👀 Reading hidden code
1.0 ms

We can now simulate our radiation ocean model, reusing much of the code from our advection-diffusion simulation.

👀 Reading hidden code
177 μs
radiation_sim = let
grid = Grid(10, 6.e6)
# you can specify non-default parameters like so:
# params = RadiationOceanModelParameters(S_mean=1500, A=210, κ=2e4)
params = RadiationOceanModelParameters()
#u, v = zeros(grid), zeros(grid)
# u, v = PointVortex(grid, Ω=0.5)
u, v = DoubleGyre(grid)
T_init_value = 10
T_init = constantT(grid; value=T_init_value)
model = RadiationOceanModel(grid, params, u, v)
Δt = 400*60*60
ClimateModelSimulation(model, copy(T_init), Δt)
end
👀 Reading hidden code
69.1 ms
image/svg+xml image/svg+xml image/svg+xml speed:
👀 Reading hidden code
605 μs
👀 Reading hidden code
802 ms

👉 Play around with the simulation to find the effect of each parameter. In particular, discover the effect of the solar insolation, S_mean, the initial temperatures, T_init, the liquid and ice albedos: α0 and αi, and the amount of emitted heat at 0°C, A.

👀 Reading hidden code
217 μs

Exercise 3.4 - Stable states

So far, we are able to set up a model and run it interactively. You see that the model quickly goes from the initial temperatures to a stable state: a state with balanced energy (radiation out, radiation in). Changing the initial state slightly will probably result in the same stable state.

But let's see what happens when we initialize with extremely high or low initial temperatures...

👉 For S=1380 (present-day value) and default parameters, does T_init_value=-50 give a different result than T_init_value=+50? What about T_init_value=+55? By trying various values for T_init_value, how many stable states do you find?

👀 Reading hidden code
460 μs

I found ...

S_1380_stable_states = md"""

I found ...

"""
👀 Reading hidden code
241 μs

👉 Answer the same question for S=1000 and S=2000.

👀 Reading hidden code
190 μs

I found ...

S_1000_stable_states = md"""

I found ...

"""
👀 Reading hidden code
192 μs

I found ...

S_2000_stable_states = md"""

I found ...

"""
👀 Reading hidden code
190 μs

Exercise 3.5

That's right, we have found a bifurcation! Under the right conditions, there are multiple stable states. But under different conditions, there is only one stable state.

Hypothesis: The multiple stable states at S=1380 are caused by the feedback effect of the ice albedo. A cold ocean reflects more heat and stays cold, a warm ocean absorbs more heat and stays warm.

👉 Find a way to confirm this hypothesis by changing the model parameters in the interactive model. Describe your findings.

👀 Reading hidden code
413 μs

Bonjour !

albedo_hypothesis_description = md"""

Bonjour !
"""
👀 Reading hidden code
190 μs

👉 Why is the number of stable states different for a lower or higher value of S?

👀 Reading hidden code
185 μs

Hi! 🍪

num_stable_states_answer = md"""

Hi! 🍪

"""
👀 Reading hidden code
192 μs

Exercise 4 - Bifurcation diagram

So far, we are able to set up a model and run it interactively, and we discovered that by changing the initial value of S_mean, we find a different number of stable states.

In this final exercise, we will generate a visualization to help us understand the relationship between S_mean and the number of stable states. Instead of running a single model interactively, we write a function that takes the model parameters as input, runs the model until equilibrium, and returns the final mean temperature. We will run this high-level function for various initial values, generating a single graph: the bifurcation diagram.

Exercise 4.1 - Equilibrium temperature

👉 Write a function eq_T that takes two argments, S and T_init_value, that sets up a radiation ocean model with S as S_mean, and with T_init_value as the constant initial temperature. Run the model until you have reached equilibrium (approximately), and return the average temperature.

👀 Reading hidden code
574 μs
eq_T (generic function with 1 method)
function eq_T(S, T_init_value)
G = Grid(10, 6.e6)
P = RadiationOceanModelParameters(κ=3e4, S_mean=S, αi=.5, A=210)
u, v = DoubleGyre(G)

IC = constantT(G; value=T_init_value)
model = RadiationOceanModel(G, P, u*2. ^U_ex, v*2. ^U_ex)
Δt = 400*60*60
sim = ClimateModelSimulation(model, copy(IC), Δt)
while (
abs(
mean(absorbed_solar_radiation(sim.T, sim.model)) * sim.model.params.C -
mean(outgoing_thermal_radiation(sim.T, sim.model)) * sim.model.params.C
) > 8.0 || sim.iteration < 1_000) && (
sim.iteration < 6_000
)
for i in 1:500
timestep!(sim)
end
end
mean(sim.T)
end
👀 Reading hidden code
2.2 ms

Hint

How do you determine that the model has reached equilibrium?

The simplest way is to use the interactive simulation above to find a fixed time/number of steps, for which any initial value has reached equilibrium.

Using a long time period is more accurate, but it means that the runtime will be long. If this is a problem, you can use a dynamic stopping condition instead. For example, you can stop the simulation early when the total incoming and total outgoing radiation are roughly equal.

👀 Reading hidden code
1.9 ms
# function eq_T(S, T_init_value)
# return missing
# end
👀 Reading hidden code
15.1 μs

👉 Use the function eq_T to verify your answer to Exercise 3.4.

👀 Reading hidden code
182 μs

👀 Reading hidden code
66.8 μs

Hint

Create an array of values for T_init_value (not too many!), and run eq_T for each. Find clusters in the results.

This is a good opportunity to check whether your implementation of eq_T is correct! Use the interactive simulation to check the results manually.

👀 Reading hidden code
299 μs

Exercise 4.2

👉 Make a bifurcation diagram. For a couple of pairs (S,T_init), calculate the equilibrium temperature. Create a scatter plot with S on the horizontal axis, and Tequilibrium on the vertical.

This calculation can take quite some time. Some tips:

  1. Start out with a small amount of points.

  2. Run the eq_T calculations in one cell, and store the result as a vector. Generate the scatter plot in a different cell.

  3. You can replace a map with ThreadsX.map to run it in parallel across multiple CPU cores. More on this below.

👀 Reading hidden code
1.6 ms
# bifurcation_ST = [(S,T) for S in 1350:10:1600 for T in [-50, 0, 50]]

bifurcation_ST = [(S,T) for S in [1000, 1380, 2000] for T in [-50, 0, 50]]
# bifurcation_ST = [(S,T) for S in 1180:100:1680 for T in [-50, 0, 50]]
👀 Reading hidden code
27.7 μs
# TODO: DELETE ME

bifurcation_result = ThreadsX.map(bifurcation_ST) do p
eq_T(p...)
end
👀 Reading hidden code
15.5 s
# TODO: DELETE ME

scatter(
first.(bifurcation_ST), bifurcation_result,
label=nothing,
xlabel="Solar insolation",
ylabel="Equilibrium temperature",
color=:black,
) |> as_svg
👀 Reading hidden code
164 ms
Parallelize a map

You use map to apply a function to each element of a vector. Why not use a for loop? One nice property of map code is that you only describe the operation for each element, not the order to run the operations in. This means that it can be easily parallelized by the computer. For example, on a computer with 4 cores, the computation map(sqrt, 1:100) can be parallelized by handling 1:25 on the first core, 26:50 on the second, etc., at the same time.

In Julia, many functional primitives (map, filter, sum, maximum, all, and more) have an automatically multithreaded version, in the ThreadsX.jl package. As a demo, compare the two cells below. (You can see the runtime in the bottom right of a cell.) You might need to run the cells a second time, the first time includes Julia's compiler doing its thing.

👀 Reading hidden code
587 μs
map(1:8) do i
sleep(.1) # to simulate an expensive computation
i ^ 2
end
👀 Reading hidden code
830 ms
ThreadsX.map(1:8) do i
sleep(.1) # to simulate an expensive computation
i ^ 2
end
👀 Reading hidden code
1.1 s

Exercise XX: Lecture transcript

(MIT students only)

Please see the link for the transcript document on Canvas. We want each of you to correct about 500 lines, but don’t spend more than 20 minutes on it. See the the beginning of the document for more instructions.

👉 Please mention the name of the video(s) and the line ranges you edited:

👀 Reading hidden code
453 μs

Abstraction, lines 1-219; Array Basics, lines 1-137; Course Intro, lines 1-144 (for example)

lines_i_edited = md"""
Abstraction, lines 1-219; Array Basics, lines 1-137; Course Intro, lines 1-144 (_for example_)
"""
👀 Reading hidden code
274 μs

Before you submit

Remember to fill in your name and Kerberos ID at the top of this notebook.

👀 Reading hidden code
408 μs

Appendix

👀 Reading hidden code
168 μs
plot_state (generic function with 1 method)
function plot_state(sim::ClimateModelSimulation; clims=(-1.1,1.1),
show_quiver=true, show_anomaly=false, IC=nothing)
model = sim.model
grid = sim.model.grid
p = plot(;
xlabel="longitudinal distance [km]", ylabel="latitudinal distance [km]",
clabel="Temperature",
yticks=( (-grid.L:1000e3:grid.L), Int64.(1e-3*(-grid.L:1000e3:grid.L)) ),
xticks=( (0:1000e3:grid.L), Int64.(1e-3*(0:1000e3:grid.L)) ),
xlims=(0., grid.L), ylims=(-grid.L, grid.L),
)
X = repeat(grid.x, grid.Ny, 1)
Y = repeat(grid.y, 1, grid.Nx)
if show_anomaly
arrow_col = :black
maxdiff = maximum(abs.(sim.T .- IC))
heatmap!(p, grid.x[:], grid.y[:], sim.T .- IC, clims=(-1.1, 1.1),
color=:balance, colorbar_title="Temperature anomaly [°C]", linewidth=0.,
size=(400,530)
)
else
arrow_col = :white
heatmap!(p, grid.x[:], grid.y[:], sim.T,
color=ice_gradient, levels=clims[1]:(clims[2]-clims[1])/21.:clims[2],
colorbar_title="Temperature [°C]", clims=clims,
linewidth=0., size=(400,520)
)
end
annotate!(p,
50e3, 6170e3,
text(
string("t = ", Int64(round(sim.iteration*sim.Δt/(60*60*24))), " days"),
color=:black, :left, 9
)
)
annotate!(p,
3000e3, 6170e3,
text(
"mean(T) = $(round(mean(sim.T), digits=1)) °C",
color=:black, :left, 9
)
)
if show_quiver
Nq = grid.N ÷ 5
quiver!(p,
X[(Nq+1)÷2:Nq:end], Y[(Nq+1)÷2:Nq:end],
quiver=grid.L*4 .*(model.u[(Nq+1)÷2:Nq:end], model.v[(Nq+1)÷2:Nq:end]),
color=arrow_col, alpha=0.7
)
end
as_png(p)
end
👀 Reading hidden code
5.3 ms
ice_gradient = PlotUtils.ContinuousColorGradient([
RGB(0.95, 0.95, 1.0), # sliver of white
RGB(0.05, 0.0, 0.3),
RGB(0.1, 0.05, 0.4),
RGB(0.4, 0.4, 0.5),
RGB(0.95, 0.7, 0.4),
RGB(1.0, 0.9, 0.3)
], [0.0, 0.001, 0.2, 0.5, 0.8, 1.0])
👀 Reading hidden code
168 ms

Function library

Just some helper functions used in the notebook.

👀 Reading hidden code
234 μs
hint (generic function with 1 method)
👀 Reading hidden code
460 μs
almost (generic function with 1 method)
👀 Reading hidden code
470 μs
still_missing (generic function with 2 methods)
👀 Reading hidden code
807 μs
keep_working (generic function with 2 methods)
👀 Reading hidden code
813 μs
👀 Reading hidden code
9.7 ms
correct (generic function with 2 methods)
👀 Reading hidden code
701 μs
not_defined (generic function with 1 method)
👀 Reading hidden code
795 μs
todo (generic function with 1 method)
👀 Reading hidden code
2.9 ms